
//  VPHTTPRequestOperationManager.m
//  VPDownloadManager
//
//  Created by vernepung on 16/3/10.
//  Copyright © 2016年 vernepung. All rights reserved.
//

#define IS_IOS10ORLATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10)

#import "VPHTTPRequestOperationManager.h"
#import "VPHTTPRequestOperationManager+Private.h"
#import "VPDownloadMacro.h"
#import "NSURLSession+VPCorrectedResumeData.h"
#import "UtilsMacro.h"

@interface VPDownloadModel ()
// 下载任务
@property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
// 手动取消当做暂停
@property (nonatomic, assign) BOOL manualCancle;
// 断点续传需要设置这个数据
@property (nonatomic, strong) NSData *resumeData;

@end
static NSString *mainPath;
@interface VPHTTPRequestOperationManager()
// 后台session configure
@property (nonatomic, strong) NSString *backgroundConfigure;
// 下载seesion会话
@property (strong, nonatomic) NSURLSession *downloadSession;
// 等待中的模型
@property (nonatomic, strong) NSMutableArray *waitingDownloadModels;
// 下载中的模型
@property (nonatomic, strong) NSMutableArray *downloadingModels;
// 下载模型字典 key = url, value = model
@property (nonatomic, strong) NSMutableDictionary *downloadingModelDic;
// 回调代理的队列
@property (strong, nonatomic) NSOperationQueue *queue;
// 文件管理
@property (nonatomic, strong) NSFileManager *fileManager;

@end
static VPHTTPRequestOperationManager *manager;
static NSLock *lock;
@implementation VPHTTPRequestOperationManager

+ (instancetype)manager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        lock = [[NSLock alloc]init];
    });
    if (!manager){
        [lock lock];
        if (manager){
            return manager;
        }
        manager = [[super alloc] init];
        [manager cancleAllBackgroundSessionTasks];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        // 创建最外层目录
        BOOL isSucc = [fileManager createDirectoryAtPath:getDownloadFolderPath(mainPath) withIntermediateDirectories:YES attributes:nil error:nil];
        isSucc = [fileManager createDirectoryAtPath:getDownloadTempFolderPath(mainPath) withIntermediateDirectories:YES attributes:nil error:nil];
        isSucc = [fileManager createDirectoryAtPath:getDownloadFilesFolderPath(mainPath) withIntermediateDirectories:YES attributes:nil error:nil];
        
        if ([fileManager fileExistsAtPath:getConfigFilePath(mainPath)]){
            manager.downloadingModelDic = [NSKeyedUnarchiver unarchiveObjectWithFile:getConfigFilePath(mainPath)];
            if (manager.downloadingModelDic.count > 0 && [manager.downloadingModelDic allKeys].count > 0){
                for (VPDownloadModel *model in [manager.downloadingModelDic allValues]) {
                    model.downloadState = [model isCompleted] ? kVPDownloadStateCompleted : kVPDownloadStatePause;
                }
            }
        }
        [lock unlock];
    }
    return manager;
}

+ (void)registerManagerWithMainName:(NSString *)mainName {
    mainPath = mainName;
}

+ (NSString *)currentMainPath {
    return mainPath;
}

- (instancetype)init {
    if (self = [super init]) {
        _backgroundConfigure = @"vp.backgroundSessionConfigurationWithIdentifier";
        _maxDownloadCount = 1;
        _resumeDownloadFIFO = YES;
        _isBatchDownload = NO;
    }
    return self;
}



- (NSURLSessionConfiguration *)defaultURLSessionConfiguration {
    NSURLSessionConfiguration *configuration = nil;
    if (_backgroundConfigure) {
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100)
        configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:_backgroundConfigure];
#else
        configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:_backgroundConfigure];
#endif
    } else{
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
    //    configuration.discretionary = YES;
    //    configuration.HTTPShouldSetCookies = YES;
    //    configuration.allowsCellularAccess = YES;
    //    configuration.HTTPMaximumConnectionsPerHost = 1;
    //    configuration.timeoutIntervalForRequest = 30.0;
    return configuration;
}

#pragma mark - 开始下载
- (void)startNewDownloadWithModel:(VPDownloadModel *)model {
    // 验证下载地址
    if (!model || !model.fileUrl) {
        DLog(@"model/dwonloadURL can't nil");
        return;
    }
    if (model.downloadState == kVPDownloadStateWaiting) {
        return;
    }
    // 验证是否已经下载文件
    if ([model isCompleted]) {
        return;
    }
    // 验证是否存在
    if (model.downloadTask && model.downloadTask.state == NSURLSessionTaskStateRunning) {
        model.downloadState = kVPDownloadStateDownloading;
        return;
    }
    // 后台下载设置
    [self configirebackgroundSessionTasksWithDownloadModel:model];
    
    [self resumeWithDownloadModel:model];
}

#pragma mark - 恢复下载
- (void)resumeWithDownloadModel:(VPDownloadModel *)downloadModel {
    if (!downloadModel) {
        return;
    }
    if (![self canResumeDownlaodModel:downloadModel]) {
        return;
    }
    if (!downloadModel.downloadTask || downloadModel.downloadTask.state == NSURLSessionTaskStateCanceling) {
        NSData *resumeData = [self resumeDataFromFileWithDownloadModel:downloadModel];
        if ([self isValideResumeData:resumeData]) {
            //走的是恢复下载
            if (IS_IOS10ORLATER) {
                downloadModel.downloadTask =  [self.downloadSession downloadTaskWithCorrectResumeData:resumeData];
            } else{
                downloadModel.downloadTask = [self.downloadSession downloadTaskWithResumeData:resumeData];
            }
        } else{
            NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadModel.fileUrl]];
            downloadModel.downloadTask = [self.downloadSession downloadTaskWithRequest:request];
        }
        downloadModel.downloadTask.taskDescription = downloadModel.mainId;
    }
    
    if (![self.downloadingModelDic objectForKey:downloadModel.mainId]) {
        self.downloadingModelDic[downloadModel.mainId] = downloadModel;
    }
    
    [downloadModel.downloadTask resume];
    downloadModel.downloadState = kVPDownloadStateDownloading;
}

#pragma mark - 暂停下载
- (void)pauseDownloadModel:(VPDownloadModel *)model {
    if (!model.manualCancle) {
        model.manualCancle = YES;
        [self cancleWithDownloadModel:model clearResumeData:NO];
    }
}

#pragma mark - 取消下载
- (void)cancleWithDownloadModel:(VPDownloadModel *)downloadModel {
    if (downloadModel.downloadState != kVPDownloadStateCompleted && downloadModel.downloadState != kVPDownloadStateError){
        [self cancleWithDownloadModel:downloadModel clearResumeData:NO];
    }
}

#pragma mark - 取消下载 是否清除之前的下载
- (void)cancleWithDownloadModel:(VPDownloadModel *)downloadModel clearResumeData:(BOOL)clearResumeData {
    if (!downloadModel.downloadTask && downloadModel.downloadState == kVPDownloadStateWaiting) {
        @synchronized (self) {
            [self.waitingDownloadModels removeObject:downloadModel];
        }
        downloadModel.downloadState = kVPDownloadStateNormal;
        return;
    }
    if (clearResumeData) {
        if (downloadModel.downloadTask && downloadModel.downloadState == kVPDownloadStateDownloading) {
            @synchronized (self) {
                [self.downloadingModels removeObject:downloadModel];
            }
        }
        downloadModel.downloadState = kVPDownloadStateNormal;
        downloadModel.resumeData = nil;
        [self deleteFileIfExist:getDownloadTempFilePath([VPHTTPRequestOperationManager currentMainPath], downloadModel.fileTempPath)];
        [downloadModel.downloadTask cancel];
    } else{
        [(NSURLSessionDownloadTask *)downloadModel.downloadTask cancelByProducingResumeData:^(NSData *resumeData){
        }];
    }
}
#pragma mark - NSURLSessionDownloadDelegate
// 恢复下载
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
    VPDownloadModel *downloadModel = [self downLoadingModelForURLString:downloadTask.taskDescription];
    if (!downloadModel || downloadModel.downloadState == kVPDownloadStatePause) {
        return;
    }
    downloadModel.readedBytes = fileOffset;
}

// 监听文件下载进度
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    VPDownloadModel *downloadModel = [self downLoadingModelForURLString:downloadTask.taskDescription];
    if (!downloadModel || downloadModel.downloadState == kVPDownloadStatePause) {
        return;
    }
    long long length = bytesWritten;
    if (length != -1){ //&& totalBytesExpectedToWrite != -1
        downloadModel.totalBytes = totalBytesExpectedToWrite;
        downloadModel.readedBytes = totalBytesWritten;
    }
}

// 下载成功
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
    VPDownloadModel *downloadModel = [self downLoadingModelForURLString:downloadTask.taskDescription];
    if (location) {
        // 移动文件到下载目录
        [self moveFileAtURL:location toPath:getDownloadFilesPath([VPHTTPRequestOperationManager currentMainPath], downloadModel.filePath)];
    }
}

// 下载完成(失败,成功)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    VPDownloadModel *downloadModel = [self downLoadingModelForURLString:task.taskDescription];
    if (!downloadModel) {
        NSData *resumeData = error ? [error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData]:nil;
        if (resumeData) {
            [resumeData writeToFile:getDownloadTempFilePath([VPHTTPRequestOperationManager currentMainPath], downloadModel.fileTempPath) atomically:YES];
        } else{
            [self deleteFileIfExist:getDownloadTempFilePath([VPHTTPRequestOperationManager currentMainPath], downloadModel.fileTempPath)];
        }
        return;
    }
    NSData *resumeData = nil;
    if (error) {
        resumeData = [error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData];
    }
    // 缓存resumeData
    if (resumeData) {
        downloadModel.resumeData = resumeData;
        [downloadModel.resumeData writeToFile:getDownloadTempFilePath([VPHTTPRequestOperationManager currentMainPath], downloadModel.fileTempPath) atomically:YES];
    } else {
        downloadModel.resumeData = nil;
        [self deleteFileIfExist:getDownloadTempFilePath([VPHTTPRequestOperationManager currentMainPath], downloadModel.fileTempPath)];
    }
    downloadModel.downloadTask = nil;
    if (downloadModel.manualCancle) {
        // 手动取消，当做暂停
        dispatch_async(dispatch_get_main_queue(), ^(){
            downloadModel.manualCancle = NO;
            downloadModel.downloadState = kVPDownloadStatePause;
            [self willResumeNextWithDowloadModel:downloadModel];
        });
    } else if (error && error.code == -999){
        // 手动暂停或者程序杀死的下载失败
        dispatch_async(dispatch_get_main_queue(), ^(){
            downloadModel.downloadState = kVPDownloadStatePause;
            [self willResumeNextWithDowloadModel:downloadModel];
        });
    } else if (error && error.code != -999){
        DLog(@"error:%@",error);
        // 下载失败
        dispatch_async(dispatch_get_main_queue(), ^(){
            downloadModel.downloadState = kVPDownloadStateError;
            [downloadModel removeFiles];
            [self willResumeNextWithDowloadModel:downloadModel];
        });
    }else{
        // 下载完成
        dispatch_async(dispatch_get_main_queue(), ^(){
            downloadModel.downloadState = kVPDownloadStateCompleted;
            [self willResumeNextWithDowloadModel:downloadModel];
        });
    }
    [manager save];
}

// 后台session下载完成
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    if (self.backgroundSessionCompletionHandler) {
        self.backgroundSessionCompletionHandler();
    }
}

#pragma mark - private function
// 配置后台后台下载session
- (void)configirebackgroundSessionTasksWithDownloadModel:(VPDownloadModel *)downloadModel
{
    if (!_backgroundConfigure) {
        return ;
    }
    
    NSURLSessionDownloadTask *task = [self backgroundSessionTasksWithDownloadModel:downloadModel];
    if (!task) {
        return;
    }
    
    downloadModel.downloadTask = task;
    if (task.state == NSURLSessionTaskStateRunning) {
        [task suspend];
    }
}

- (NSURLSessionDownloadTask *)backgroundSessionTasksWithDownloadModel:(VPDownloadModel *)downloadModel
{
    NSArray *tasks = [self sessionDownloadTasks];
    for (NSURLSessionDownloadTask *task in tasks) {
        if (task.state == NSURLSessionTaskStateRunning || task.state == NSURLSessionTaskStateSuspended) {
            if ([downloadModel.mainId isEqualToString:task.taskDescription]) {
                return task;
            }
        }
    }
    return nil;
}
// 获取所以的后台下载session
- (NSArray *)sessionDownloadTasks {
    __block NSArray *tasks = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self.downloadSession getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        tasks = downloadTasks;
        dispatch_semaphore_signal(semaphore);
    }];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    return tasks;
}

// 判断下载模型是否存在归档当中
- (VPDownloadModel *)isExistsModel:(VPDownloadModel *)model{
    if (self.downloadingModelDic && self.downloadingModelDic.allKeys.count > 0) {
        NSArray *searchResultArray = [[self.downloadingModelDic allValues] filteredArrayUsingPredicate:[self getPredicateWithMainId:[model mainId]]];
        if (searchResultArray.count > 0){
            return searchResultArray[0];
        }else{
            return nil;
        }
    }else{
        return nil;
    }
}

//文件移动
- (void)moveFileAtURL:(NSURL *)srcURL toPath:(NSString *)dstPath{
    if (!dstPath) {
        DLog(@"error filePath is nil!");
        return;
    }
    NSError *error = nil;
    if ([self.fileManager fileExistsAtPath:dstPath] ) {
        [self.fileManager removeItemAtPath:dstPath error:&error];
        if (error) {
            DLog(@"removeItem error %@",error);
        }
    }
    NSURL *dstURL = [NSURL fileURLWithPath:dstPath];
    [self.fileManager moveItemAtURL:srcURL toURL:dstURL error:&error];
    if (error){
        DLog(@"moveItem error:%@",error);
    }
}

//文件删除
- (void)deleteFileIfExist:(NSString *)filePath {
    if ([[NSFileManager defaultManager] fileExistsAtPath:filePath] ) {
        NSError *error  = nil;
        [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
        if (error) {
            NSLog(@"removeItem： %@",error);
        }
    }
}

//寻找下一个下载
- (void)willResumeNextWithDowloadModel:(VPDownloadModel *)downloadModel {
    if (_isBatchDownload) {
        return;
    }
    @synchronized (self) {
        [self.downloadingModels removeObject:downloadModel];
        // 还有未下载的
        if (self.waitingDownloadModels.count > 0) {
            [self resumeWithDownloadModel:_resumeDownloadFIFO ? self.waitingDownloadModels.firstObject:self.waitingDownloadModels.lastObject];
        }
    }
}

- (BOOL)canResumeDownlaodModel:(VPDownloadModel *)downloadModel {
    if (_isBatchDownload) {
        return YES;
    }
    @synchronized (self) {
        if (self.downloadingModels.count >= _maxDownloadCount ) {
            if ([self.waitingDownloadModels indexOfObject:downloadModel] == NSNotFound) {
                [self.waitingDownloadModels addObject:downloadModel];
                self.downloadingModelDic[downloadModel.mainId] = downloadModel;
            }
            downloadModel.downloadState = kVPDownloadStateWaiting;
            return NO;
        }
        if ([self.waitingDownloadModels indexOfObject:downloadModel] != NSNotFound) {
            [self.waitingDownloadModels removeObject:downloadModel];
        }
        if ([self.downloadingModels indexOfObject:downloadModel] == NSNotFound) {
            [self.downloadingModels addObject:downloadModel];
        }
        return YES;
    }
}

- (NSData *)resumeDataFromFileWithDownloadModel:(VPDownloadModel *)downLoadModel {
    if (downLoadModel.resumeData) {
        return downLoadModel.resumeData;
    }
    if ([[NSFileManager defaultManager] fileExistsAtPath: getDownloadTempFilePath([VPHTTPRequestOperationManager currentMainPath], downLoadModel.fileTempPath)]) {
        NSData *resumeData = [NSData dataWithContentsOfFile:getDownloadTempFilePath([VPHTTPRequestOperationManager currentMainPath], downLoadModel.fileTempPath)];
        return resumeData;
    }
    return nil;
}

- (BOOL)isValideResumeData:(NSData *)resumeData {
    if (!resumeData || resumeData.length == 0) {
        return NO;
    }
    return YES;
}

#pragma mark - public
- (void)configureBackroundSession {
    if (!_backgroundConfigure) {
        return;
    }
    [self downloadSession];
}
// 取消所有后台
- (void)cancleAllBackgroundSessionTasks
{
    if (!_backgroundConfigure) {
        return;
    }
    for (NSURLSessionDownloadTask *task in [self sessionDownloadTasks]) {
        [task cancelByProducingResumeData:^(NSData * resumeData) {
        }];
    }
}
//删除下载
- (void)removeExistsModel:(VPDownloadModel *)model{
    if (!model || !model.fileTempPath) {
        return;
    }
    for (VPDownloadModel *downLoadModel in [self.downloadingModelDic allValues]) {
        if ([downLoadModel.mainId isEqualToString:model.mainId]) {
            [self cancleWithDownloadModel:downLoadModel clearResumeData:YES];
            [self removeDownLoadingModelForURLString:downLoadModel.mainId];
        }
        [downLoadModel removeFiles];
    }
    [self save];
}

- (void)cancelAllDownloads{
    @synchronized (self) {
        for (VPDownloadModel *downloadModel in [self.downloadingModelDic allValues]) {
            if (downloadModel.downloadState == kVPDownloadStateDownloading) {
                [self pauseDownloadModel:downloadModel];
            }else if (downloadModel.downloadState == kVPDownloadStateWaiting) {
                [self cancleWithDownloadModel:downloadModel clearResumeData:NO];
            }
        }
    }
}

- (void)restartAllDownloads{
    @synchronized (self) {
        for (VPDownloadModel *downloadModel in [self.downloadingModelDic allValues]) {
            if (downloadModel.downloadState != kVPDownloadStateDownloading) {
                [manager startNewDownloadWithModel:downloadModel];
            }
        }
    }
}

- (void)save{
    @try {
        [NSKeyedArchiver archiveRootObject:self.downloadingModelDic toFile:getConfigFilePath(mainPath)];
    }
    @catch (NSException *exception) {
        DLog(@"save download config file fail!!");
    }
}
//重置下载模型字典
- (void)clear{
    [manager cancelAllDownloads];
    [manager.downloadingModelDic removeAllObjects];
    manager.downloadingModelDic = nil;
    [self.fileManager removeItemAtPath:getConfigFilePath(mainPath) error:nil];
    [self.fileManager removeItemAtPath:getDownloadTempFolderPath(mainPath) error:nil];
    [self.fileManager removeItemAtPath:getDownloadFilesFolderPath(mainPath) error:nil];
    [self save];
}

//根据下载状态获取下载模型 （只能获取 进下载中心的）
- (NSArray<VPDownloadModel *> *)getModelsWithState:(kVPDownloadState)state{
    NSArray *resultArray = [[self.downloadingModelDic allValues] filteredArrayUsingPredicate:[self getPredicateWithState:state]];
    NSMutableArray *downloadCenterResultArray = [NSMutableArray array];
    for (VPDownloadModel *model in resultArray) {
        if (model.isDownloadCenterValid) {
            [downloadCenterResultArray addObject:model];
        }
    }
    return downloadCenterResultArray;
}


//根据标识删除下载模型
- (void)removeDownLoadingModelForURLString:(NSString *)URLString {
    if ([[self.downloadingModelDic allKeys] containsObject:URLString]) {
        [self.downloadingModelDic removeObjectForKey:URLString];
    }
}
//根据标识获取下载模型
- (VPDownloadModel *)downLoadingModelForURLString:(NSString *)URLString {
    return [self.downloadingModelDic objectForKey:URLString];
}

#pragma mark - getter & setter
- (NSFileManager *)fileManager {
    if (!_fileManager) {
        _fileManager = [NSFileManager defaultManager];
    }
    return _fileManager;
}

- (NSURLSession *)downloadSession {
    if (!_downloadSession) {
        _downloadSession = [NSURLSession sessionWithConfiguration:[self defaultURLSessionConfiguration] delegate:self delegateQueue:self.queue];
    }
    return _downloadSession;
}

- (NSOperationQueue *)queue {
    if (!_queue) {
        _queue = [[NSOperationQueue alloc]init];
        _queue.maxConcurrentOperationCount = 1;
    }
    return _queue;
}

- (NSMutableDictionary *)downloadingModelDic {
    if (!_downloadingModelDic) {
        _downloadingModelDic = [NSMutableDictionary dictionary];
    }
    return _downloadingModelDic;
}

- (NSMutableArray *)waitingDownloadModels {
    if (!_waitingDownloadModels) {
        _waitingDownloadModels = [NSMutableArray array];
    }
    return _waitingDownloadModels;
}

- (NSMutableArray *)downloadingModels {
    if (!_downloadingModels) {
        _downloadingModels = [NSMutableArray array];
    }
    return _downloadingModels;
}

@end
